Black Friday Sale Upgrade Your Home →

Passing the Store Down Implicitly via Context

Passing the Store Down Implicitly via Context

In the last section we refactored our code to pass store around as a prop. This requires a lot of boilerplate code.

However, there is an easier way: use React's context feature.

To illustrate, we'll start by creating a new Provider component. From its render() method, it just returns what its child is. This means we can wrap any component in a Provider, and it's going to render that component.

JAVASCRIPT
class Provider extends Component {
render() {
return this.props.children;
}
}

Now we need to change our ReactDOM.render() call to render TodoApp inside of our new Provider. We also now pass state as a prop to Provider instead of TodoApp.

JAVASCRIPT
ReactDOM.render(
<Provider store={createStore(todoApp)}>
<TodoApp />
</Provider>,
document.getElementById('root')
);

Now we will update our Provider component to use React's context feature. This is what makes the store available to any component that it renders. The store now be available for any of the children and grandchildren of the components Provider renders (in our example, this is TodoApp and all of the other components and containers we've built inside of it!)

There is an important condition that must be added to make it work. We have to provide childContextTypes on the component that provides child context. This is similar to React's PropTypes definition that helps you when writing your app, except that in this case it is required in order for the child components to receive context.

JAVASCRIPT
class Provider extends Component {
getChildContext() {
return {
store: this.props.store // This corresponds to the `store` passed in as a prop
};
}
render() {
return this.props.children;
}
}
Provider.childContextTypes = {
store: React.PropTypes.object
}

Refactor Components to get store from context instead of props

In each of our components, we need to change how state is received.

JAVASCRIPT
class VisibleTodoList extends Component {
componentDidMount() {
const { store } = this.context;
this.unsubscribe = store.subscribe(() =>
this.forceUpdate()
);
}
.
. // Rest of component as before to `render()` method
.
render() {
const props = this.props;
const { store } = this.context;
const state = store.getState();
.
.
.
}
}

Note: The context is opt-in for all components, so we have to specify contextTypes. If you don't specify this, the component won't received the relevant context, so it is essential to declare them!

JAVASCRIPT
VisibleTodoList.contextTypes = {
store: React.PropTypes.object
}

Updating our Functional Components for context

Our functional components don't have this, so how will we give them context?

It turns out that they receive context as a second argument (after props). We also need to add contextTypes for the component that specifies which context we want to receive (in this case store from Provider). Context can be passed down to any level, so you can think of it as creating a 'wormhole' to whatever component that uses it, however, you must remember to opt-in by declaring the contextTypes

JAVASCRIPT
const AddTodo = (props, { store }) => {
.
. // Rest of component as before
.
}
AddTodo.contextTypes = {
store: React.PropTypes.object
}

We must also convert FilterLink to accept the store from context and provide the contextTypes

Updating FilterLink
JAVASCRIPT
class FilterLink extends Component {
componentDidMount() {
const { store } = this.context;
this.unsubscribe = store.subscribe(() =>
this.forceUpdate()
);
}
.
. // Rest of component as before down to `render()`
.
render() {
const props = this.props;
const { store } = this.context;
const state = store.getState();
.
. // Rest of component as before
.
}
}
FilterLink.contextTypes = {
store: React.PropTypes.object
}

Note: Footer and Individual <FilterLink> elements don't need store as a prop anymore as we don't need to pass it down, so we can remove that from the props:

JAVASCRIPT
const Footer = () => {
<p>
<FilterLink
filter='SHOW_ALL'
>
All
</FilterLink>
.
. // Follow this pattern for the other `FilterLink` component references
.
}

We can also get rid of the store prop for TodoApp because it no longer needs to be passed down to the containers.

JAVASCRIPT
const TodoApp = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)

Recap

Now we are implictly passing store down via context instead of having to explicitly do it via props.

Context is a powerful feature, but in a way it contradicts the React philosophy of having an explicit data flow. The context essentially allows global variables across the component tree. Global variables are usually a bad idea, and you shouldn't use the context feature in this way. You should only use context if you're using it for dependency injection (like in our case where we need to make a single object available to all components).

Finally, it is important to note that the context API is not stable in React. It has changed before, and it is likely to change again, so it is probably best to not rely on it too much.

01. The Single Immutable State Tree

The first principle of Redux (no matter the complexity):

The entire state of the application will be represented by one JavaScript object.

All changes and mutations to the application are explicit. These mutations, which include the data and the UI state, are contained in a single object we call the state.

Since the entire state is represented in a single object, we are able to keep track of changes over time

02. Describing State Changes with Actions

The second principle of Redux is that the state tree is read only. Any time you want to change the state, you have to dispatch an action. An action is a plain JS object describing the change. Just like the state is the minimal representation of the data, the action is the minimal representation of the change to that data.

The only requirement for an action is that it has a type property (conventionally a String).

For example, in a counter app, there are INCREMENT and DECREMENT actions. In the case of a ToDo app, the display components don't know how an item was added to the list-- all they know is that an ADD_TODO action was dispatched, with text content "hey" and a sequential id.

The overall principle here is that the state is read only, and can only be modified by dispatching actions.

03. Pure and Impure Functions

Before learning more about Redux, it's important to know the difference between "Pure" and "Impure" functions.

Pure:

JAVASCRIPT
function square(x) {
return x * x;
}
function squareAll(items) {
return items.map(square);
}

Pure functions are those whose return values depend only upon the values of their arguments. Pure functions don't have side effects like network or database calls. Pure functions also do not override the values of anything. In the above example, a new array is returned instead of modifying the items that was passed in.

Impure:

JAVASCRIPT
function square(x) {
updateXInDatabase(x);
return x * x;
}
function squareAll(items) {
for (let i = 0; i < items.length; i++) {
items[i] = square(items[i]);
}
}

Contrast the "Impure" function. A database is called, and values passed in are being overwritten.

This distinction is important to understand, since Redux requires that certain functions are pure.

04. The Reducer Function

React introduced the idea that the UI layer is most predictable when it is described as a pure function of the application's state.

Redux compliments this approach by requiring that state mutations in your app need to be described by a pure function that takes the previous state and the action being dispatched, and returns the next state of your application.

Inside a Redux application there is one particular function that takes the previous state and the action being dispatched, and returns the next state of the whole application. It is important that the function is pure (i.e. the state being given to it isn't modified) because it has to return the new object representing the application's new state.

Even in large applications, there is still just a single function that calculates the new state of the application. It doesn't have to be slow-- if certain parts of the state haven't changed, their references can stay as-is. In the ToDo app example, when changing the visibility between "All/Completed/Active" the actual items themselves haven't changed, so the reference to the previous version of the todos array is left intact.

This is the 3rd and final principle of Redux: to describe state mutations you have to write a function that takes the previous state of the app and the action being dispatched, then returns the next state of the app. This function is called the Reducer.

Passing the Store Down with <Provider> from React Redux

In the last section, we implemented a Provider component to pass store implicitly with React's context feature. It was really convenient.

In fact, it was so convenient that we don't need to write Provider ourselves-- we can import it from the react-redux library that gives React bindings to the Redux library.

Start by importing Provider from 'react-redux'

JAVASCRIPT
// CDN style
const { Provider } = ReactRedux;
// npm style
import { Provider } from 'react-redux';

Just like the Provider we wrote before, the Provider that comes with react-redux exposes the store as a prop on the context.

  Previous      Next